﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SceneLib
{
    public class SceneTriangle : SceneObject
    {
        public List<Vector> Vertex { get; set; }
        public List<Vector> VertexClip { get; set; }
        public List<Vector> Normal { get; set; }
        public List<Vector> Colors { get; set; }
        public List<SceneMaterial> Materials { get; set; }
        public List<float> U { get; set; }
        public List<float> V { get; set; }

        public bool InClipCoords = false;

        public SceneTriangle()
        {
            Vertex = new List<Vector>();
            VertexClip = new List<Vector>();
            Normal = new List<Vector>();
            Materials = new List<SceneMaterial>();
            Colors = new List<Vector>();
            U = new List<float>();
            V = new List<float>();            
        }

        public override Intersection Intersect(Vector e, Vector d, float t0, float tf, float time0, float timef)
        {
            Vector a = Vertex[0].Rotate(Rotation).Scale(Scale).Translate(Position);
            Vector b = Vertex[1].Rotate(Rotation).Scale(Scale).Translate(Position);
            Vector c = Vertex[2].Rotate(Rotation).Scale(Scale).Translate(Position);

            float denominator = (a.x - b.x) * (d.z * (a.y - c.y) - d.y * (a.z - c.z))
                                - (a.x - c.x) * (d.z * (a.y - b.y) - d.y * (a.z - b.z))
                                + d.x * ((a.y - b.y) * (a.z - c.z) - (a.y - c.y) * (a.z - b.z));

            if (denominator == 0)
                return Intersection.NOINTERSECT;

            float t = ((a.x - b.x) * ((a.y - c.y) * (a.z - e.z) - (a.y - e.y) * (a.z - c.z))
                    - (a.x - c.x) * ((a.y - b.y) * (a.z - e.z) - (a.y - e.y) * (a.z - b.z))
                    + (a.x - e.x) * ((a.y - b.y) * (a.z - c.z) - (a.y - c.y) * (a.z - b.z))) / denominator;

            if (t < t0 || t > tf)
                return Intersection.NOINTERSECT;

            float gamma = ((a.x - b.x) * (d.z * (a.y - e.y) - d.y * (a.z - e.z))
                    - (a.x - e.x) * (d.z * (a.y - b.y) - d.y * (a.z - b.z))
                    + d.x * ((a.y - b.y) * (a.z - e.z) - (a.y - e.y) * (a.z - b.z))) / denominator;

            if (gamma < 0 || gamma > 1)
                return Intersection.NOINTERSECT;

            float beta = ((a.x - e.x) * (d.z * (a.y - c.y) - d.y * (a.z - c.z))
                    - (a.x - c.x) * (d.z * (a.y - e.y) - d.y * (a.z - e.z))
                    + d.x * ((a.y - e.y) * (a.z - c.z) - (a.y - c.y) * (a.z - e.z))) / denominator;

            if (beta < 0 || beta > 1 - gamma)
                return Intersection.NOINTERSECT;

            float alfa = 1 - beta - gamma;
            Vector p = e + t * d;
            SceneMaterial material = new SceneMaterial
            {
                Diffuse = alfa * Materials[0].Diffuse + beta * Materials[1].Diffuse + gamma * Materials[2].Diffuse,
                Reflective = (Materials[0].Reflective == null ||
                                    Materials[1].Reflective == null ||
                                    Materials[2].Reflective == null) ? null
                                    : alfa * Materials[0].Reflective + beta * Materials[1].Reflective + gamma * Materials[2].Reflective,
                RefractionIndex = (Materials[0].RefractionIndex == null || 
                                    Materials[1].RefractionIndex == null ||
                                    Materials[2].RefractionIndex == null ) ? null
                                    : alfa * Materials[0].RefractionIndex + beta * Materials[1].RefractionIndex + gamma * Materials[2].RefractionIndex,
                Shininess = alfa * Materials[0].Shininess + beta * Materials[1].Shininess + gamma * Materials[2].Shininess,
                Specular = alfa * Materials[0].Specular + beta * Materials[1].Specular + gamma * Materials[2].Specular,
                Transparent = (Materials[0].Transparent == null ||
                                    Materials[1].Transparent == null ||
                                    Materials[2].Transparent == null) ? null
                                    : alfa * Materials[0].Transparent + beta * Materials[1].Transparent + gamma * Materials[2].Transparent,
                Name = Materials[0].Name,
                TextureFile = Materials[0].TextureFile,
                TextureImage = Materials[0].TextureImage
            };

            Vector n = alfa * Normal[0].Rotate(Rotation) + beta * Normal[1].Rotate(Rotation) + gamma * Normal[2].Rotate(Rotation);     
            
            n.Normalize3();

            if (Vector.Dot3(n, d) > 0)
                n = -1f * n;

            return new Intersection
            {
                IsIntersected = true,
                T = t,
                Material = material,
                P = p,
                Normal = n,
                ObjectScene = this                
            };
        }

        public override Vector GetTextureColor(Vector p)
        { 
            if (Materials[0].TextureImage != null &&
                Materials[1].TextureImage != null &&
                Materials[2].TextureImage != null)
            {
                Vector bar = MathUtils.Barycentric3D(p, Vertex[0].Rotate(Rotation).Scale(Scale).Translate(Position),
                                                        Vertex[1].Rotate(Rotation).Scale(Scale).Translate(Position),
                                                        Vertex[2].Rotate(Rotation).Scale(Scale).Translate(Position));
                float alfa = bar.x;
                float beta = bar.y;
                float gamma = bar.z;
                float u = alfa * U[0] + beta * U[1] + gamma * U[2];
                float v = alfa * V[0] + beta * V[1] + gamma * V[2];                

                Vector colora = GetColorFromTextureVertex(u, v, Materials[0]);
                Vector colorb = GetColorFromTextureVertex(u, v, Materials[1]);
                Vector colorc = GetColorFromTextureVertex(u, v, Materials[2]);

                return (alfa * colora + beta * colorb + gamma * colorc).Clamp3();
            }
            else
                return new Vector(1, 1, 1);
        }

        private Vector GetColorFromTextureVertex(float u, float v, SceneMaterial material)
        {
            int i, j;
            int height = material.TextureImage.Height;
            int width = material.TextureImage.Width;
            float deltaU = Convert.ToSingle(u * (width - 1) - Math.Floor(u * (width - 1)));
            float deltaV = Convert.ToSingle(v * (height - 1) - Math.Floor(v * (height - 1))); 

            i = Convert.ToInt32(Math.Floor(u * (material.TextureImage.Width - 1)));
            j = Convert.ToInt32(Math.Floor(v * (material.TextureImage.Height - 1)));
            return (1 - deltaU) * (1 - deltaV) * material.GetTexturePixelColor(i, j) +
                                deltaU * (1 - deltaV) * material.GetTexturePixelColor((i + 1) % width, j) +
                                deltaV * (1 - deltaU) * material.GetTexturePixelColor(i, (j + 1) % height) +
                                deltaV * deltaU * material.GetTexturePixelColor((i + 1) % width, (j + 1) % height);
        }
    }
}
